package service;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import model.User;
import org.apache.commons.beanutils.BeanUtils;
import exceptions.DataException;
import exceptions.LeagueRestrictionException;
import exceptions.UserNotFoundException;

/**
 * System users may be created. 
 * All maintenance operations (CRUD - Create/Read/Update/Delete) must be provided.
 * 
 * User attributes during creation are:
 * First name
 * Last name
 * Home phone
 * Work phone
 * Email
 * A picture may be uploaded and associated with the player
 * @author Andre Melo
 * @author Marcus Leite 
 * @author Sergio Danilo
 *
 */
public class UserServiceImpl implements UserService {
	
	/*
	 * The Entity Manager, responsible for the operations with the database.
	 */
	@PersistenceContext
	private EntityManager em;

	@SuppressWarnings("unchecked")
	@Override
	public void changeUserAttribute(String id, String attribute, String value) throws IllegalAccessException, InvocationTargetException, DataException{
		if(attribute == null || attribute.equals("")){
			throw new DataException("Must provide an attribute to be changed");
		}
		if (attribute.equals("firstName")) {
			verifyAttributeEmpty(attribute, value, "Required data: first name");
		}else
		if (attribute.equals("lastName")) {
			verifyAttributeEmpty(attribute, value, "Required data: last name");
		} else
		if (attribute.equals("homePhone")) {
			verifyAtLeastOnePhone(id, attribute, value);
		} else
		if (attribute.equals("workPhone")) {
			verifyAtLeastOnePhone(id, attribute, value);
		} else
		if (attribute.equals("cellPhone")) {
			verifyAtLeastOnePhone(id, attribute, value);
		} else
		if (attribute.equals("email")) {
			verifyAttributeEmpty(attribute, value, "Required data: email");
			if (!IsEmailMine(id, value)){
				verifyEmailExistence(value);	
			}			
		} else
		if (attribute.equals("picture")) {
		} else {
			throw new DataException("Unknown user attribute");
		}		
		Query query = em.createQuery("Select u From User u Where u.id = :id");
		query.setParameter("id",Integer.parseInt(id));
		List<User> user = query.getResultList();
		BeanUtils.setProperty(user.get(0), attribute, value);
	}

	@Override
	public Integer createUser(String firstName, String lastName, String homePhone, String workPhone, 
		                   String cellPhone, String email, String picture, String password, String role) throws DataException{
		verifyUserAttributes(firstName, lastName, homePhone, workPhone, cellPhone, email, picture);
		User newUser = new User(firstName,lastName, homePhone, workPhone, cellPhone, email, picture, password, role);
		return save(newUser);
	}

	@Override
	public User delete(Integer id) throws LeagueRestrictionException {
		User user = findById(id);
		if (user.isOperator())
			throw new LeagueRestrictionException("Cannot remove league operator");
		if (user != null)
			em.remove(user);
		return user;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void deleteAll() {
		Query query = em.createQuery("Select u From User u");
		List<User> userList = query.getResultList();
		int listSize = userList.size();
		for (int i = 0; i < listSize; i++)
			em.remove(userList.get(i));
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public User findByEmail(String email) throws UserNotFoundException {
		Query query = em.createQuery("Select u From User u Where u.email = :email");
		query.setParameter("email", email);
		List<User> users = query.getResultList();
		if(users.isEmpty())
			throw new UserNotFoundException("Cannot find user.");		
		return users.get(0);
	}

	@Override
	public User findById(Integer id) {
		return em.find(User.class, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<User> findByLastName(String lastName) throws UserNotFoundException {
		Query query = em.createQuery("Select u From User u");
		List<User> users = query.getResultList();
		List<User> usersResultList = new ArrayList<User>();
		for (User user: users){
			if (user.getLastName().toLowerCase().equals(lastName.toLowerCase())) {
				usersResultList.add(user);
			} else if (user.getLastName().toLowerCase().matches(lastName)){
				usersResultList.add(user);
			}
		}
		if (usersResultList.isEmpty())
			throw new UserNotFoundException("Could not find user "+lastName);
		return usersResultList;			
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public String getUserAttribute(String id, String attribute) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Query query = em.createQuery("Select u From User u Where u.id = :id");
		query.setParameter("id",Integer.parseInt(id));
		List<User> user = query.getResultList();
		return BeanUtils.getProperty(user.get(0), attribute);
	}

	/*
	 * Verify if email and user match
	 * @param userId The ID of the user
	 * @param email The e-mail of the user
	 * @return True if match, false otherwise
	 */
	private boolean IsEmailMine(String userId, String email ){
		User user = findById(Integer.parseInt(userId)); 
		String userEmail = user.getEmail();
		return userEmail.equals(email);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean isAnExistentUser(Integer userId){
		Query query = em.createQuery("Select u From User u Where u.id = :id");
		query.setParameter("id",userId);
		List<User> user = query.getResultList(); 
		if (user.isEmpty())
			return false;
		return true;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<User> listAll() {
		Query query = em.createQuery("Select u From User u Order By u.firstName");
		return query.getResultList();
	}

	@Override
	public Integer save(User user) {
		em.persist(user);
		return user.getId();
	}

	@Override
	public void update(User user) {
		em.merge(user);
	}
	
	/*
	 * Verify if an user have at least one way of contact.
	 * @param id Id of the user.
	 * @param attribute The attribute to be verified.
	 * @param value The value of the attribute to be verified.
	 * @throws UserException If the user does not have at least one way of contact.
	 */
	private boolean verifyAtLeastOnePhone(String id, String attribute, String value) throws DataException {
		User user = findById(Integer.parseInt(id));
		String phoneHome = user.getHomePhone();
		String workPhone = user.getWorkPhone();
		String cellPhone = user.getCellPhone();
		if (value == null || value.equals("")) {
			if (attribute.equals("homePhone"))
				phoneHome = value;
			if (attribute.equals("cellPhone"))
				cellPhone = value;
			if (attribute.equals("workPhone"))
				workPhone = value;
		}
		if ((phoneHome == null || phoneHome.equals("")) &&
			(workPhone == null || workPhone.equals("")) &&
			(cellPhone == null || cellPhone.equals("")))
				throw new DataException("Need at least one phone");
		else return true;	
	}

	/*
	 * Verify if the first name and last name was provided.
	 * @param attribute Last name or first name.
	 * @param value Value of first name or last name.
	 * @param message Specific exception message.
	 * @throws UserException Exception with the correct message.
	 */
	@SuppressWarnings("unused")
	private void verifyAttributeEmpty(String attribute, String value, String message) throws DataException {
		if (value == null || value.equals("")) {
			throw new DataException(message);
		}
	}

	/*
	 * Verify if the data required are null or empty 
	 * @param firstName First name of the user
	 * @param lastName Last name of the user
	 * @param email E-mail of the user
	 * @throws DataException
	 */
	private void verifyDataRequired(String firstName, String lastName, String email) throws DataException {
		String message = "Required data: ";
		boolean error = false;
		if (firstName == null || firstName.equals("")) {
			error = true;
			message += "first name";
		}
		if (lastName == null || lastName.equals("")) {
			if (error == true)
				message += ", ";
			error = true;
			message += "last name";
		}
		if (email == null || email.equals("")) {
			if (error == true)
				message += ", ";
			error = true;
			message += "email";
		}	
		if (error == true) 
			throw new DataException(message);
	}
	
	/*
	 * Verify if an e-mail already exists.
	 * @param email E-mail to be verified.
	 * @throws UserException If the e-mail already exists.
	 */
	@SuppressWarnings("unchecked")
	private void verifyEmailExistence(String email) throws DataException {
		if (!email.equals("") && email != null){
			Query query = em.createQuery("Select u From User u Where u.email = :email");
			query.setParameter("email", email);
			List<User> users = query.getResultList();						
			if (!(users.isEmpty()))
				throw new DataException("User with this email exists");
		}
	}	
	
	/*
	 * Verify all attributes for the correct creation of an user.
	 * @param firstName First name of the user.
	 * @param lastName Last name of the user.
	 * @param homePhone Home phone of the user.
	 * @param workPhone Work phone of the user.
	 * @param cellPhone Cell phone of the user.
	 * @param email E-mail of the user.
	 * @param picture Name of the archive of the user's picture.
	 * @throws DataException 
	 * @throws AttributeException 
	 * @throws UserException An specific exception message.
	 */
	private void verifyUserAttributes(String firstName, String lastName, String homePhone, String workPhone, 
            String cellPhone, String email, String picture) throws DataException {
		verifyDataRequired(firstName, lastName, email);
		verifyWaysOfContact(homePhone, cellPhone, workPhone);
		verifyEmailExistence(email);
	}
	
	/*
	 * Verify if at least one phone was correctly created
	 * @param homePhone Home phone of the user
	 * @param cellPhone Cell phone of the user
	 * @param workPhone Work phone of the user
	 * @param email E-mail of the user
	 * @throws UserException
	 */
	private void verifyWaysOfContact(String homePhone, String cellPhone,
			String workPhone) throws DataException {
		if ((homePhone == null || homePhone.equals("")) && (workPhone == null || workPhone.equals("")) && 
			    (cellPhone == null || cellPhone.equals("")))
				throw new DataException("Need at least one phone");
	}
	
}
